using AnimCollections;
using System;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
using UnityEngine;

namespace DA.AnimGraph {
    public struct DryParam {
        public ulong baseAddr; // 偏移基础位置

        // 需要操作的指针必然在以下两个参数决定的范围内（左闭右开），用于检查
        public ulong bufferStartAddr;
        public ulong bufferEndAddr;
    }

    internal static unsafe class DryUtility {
        public static void DryPtr(ref void* src, DryParam dryParam) {
            Debug.Assert(IsInRange((ulong)src, dryParam.bufferStartAddr, dryParam.bufferEndAddr), "[Det] ");
            // 把原内容换成地址偏移
            ulong addr = (ulong)src - dryParam.baseAddr;
            src = (void*)addr;
        }

        public static void WetPtr(ref void* src, DryParam dryParam) {
            ulong offset = (ulong)src;
            ulong addr = offset + dryParam.baseAddr;
            src = (void*)addr;
        }

        public static void DryPerksPtrList<T>(ref UnsafePtrList<T> srcRef, DryParam dryParam) where T : unmanaged {
            var src = (UnsafePtrList<T>*)UnsafeUtility.AddressOf(ref srcRef);
            // 24字节可用
            // 需要：长度4 + 第一个元素位置8

            int length = src->Length;
            // 检查
            for (int i = 1; i < length; i++) {
                // 检查元素是否紧密排列
                Debug.Assert((ulong)(src->Ptr[i]) - (ulong)(src->Ptr[i - 1]) == (ulong)sizeof(T), "[Dry] 元素未按要求排列");
            }

            // 必要信息拿出来，然后释放原数据
            if (length == 0) {
                var srcL2 = (ulong*)src;
                srcL2[0] = (ulong)length;
                return;
            }

            var firstElemAddr = (*(src->Ptr));
            srcRef.Dispose();

            var srcL = (ulong*)src;
            srcL[0] = (ulong)length;
            srcL[1] = (ulong)firstElemAddr - dryParam.baseAddr;
        }

        public static void WetPerksPtrList<T>(ref UnsafePtrList<T> srcRef, DryParam dryParam) where T : unmanaged {
            var src = (UnsafePtrList<T>*)UnsafeUtility.AddressOf(ref srcRef);

            var srcL = (ulong*)src;
            int length = (int)srcL[0];
            if (length == 0) {
                return;
            }

            ulong firstEleAddr = srcL[1];

            // 因为知道是 NodeLink.perks 所以这么写
            srcRef = new UnsafePtrList<T>(length, Allocator.Persistent);
            for (int i = 0; i < length; i++) {
                ulong offset = firstEleAddr + (ulong)(i * sizeof(T));
                src->Ptr[i] = (T*)(dryParam.baseAddr + offset);
            }
            srcRef.Length = length;
        }

        public static void DryTransitionConditionLinkArray(ref NativeSimpleArray<TransitionConditionLink> array, DryParam dryParam) {
            // 先处理内部
            for (int i = 0; i < array.Length; i++) {
                array.GetRefAt(i).Dry(dryParam);
            }
            // 再处理自己
            DryArray(ref array, dryParam);
        }
        public static void WetTransitionConditionLinkArray(ref NativeSimpleArray<TransitionConditionLink> array, DryParam dryParam) {
            // 先处理自己
            WetArray(ref array, dryParam);
            // 再处理内部
            for (int i = 0; i < array.Length; i++) {
                array.GetRefAt(i).Wet(dryParam);
            }
        }

        public static void DryPtrArray(ref NativeSimpleArray<IntPtr> srcRef, DryParam dryParam) {
            var src = (NativeSimpleArray<IntPtr>*)UnsafeUtility.AddressOf(ref srcRef);
            int length = src->Length;
            if (length == 0) {
                return;
            }
            ulong* srcElem = (ulong*)src->Ptr; // 存储地址的数组
            for (int i = 0; i < length; i++) {
                // 确保原内容都在buffer内
                Debug.Assert(IsInRange(srcElem[i], dryParam.bufferStartAddr, dryParam.bufferEndAddr), "[Dry] ");
                // 把原内容换成地址偏移
                ulong writePos = srcElem[i] - dryParam.baseAddr;
                srcElem[i] = writePos;
            }
            // 自己内部指针偏移
            ulong offset = (ulong)src->Ptr - dryParam.baseAddr;
            src->UnsafeSetBufferPtr(offset);
        }

        public static void WetPtrArray(ref NativeSimpleArray<IntPtr> srcRef, DryParam dryParam) {
            var src = (NativeSimpleArray<IntPtr>*)UnsafeUtility.AddressOf(ref srcRef);
            int length = src->Length;
            if (length == 0) {
                return;
            }
            // 自己内部指针偏移
            ulong offset = (ulong)src->Ptr + dryParam.baseAddr;
            src->UnsafeSetBufferPtr(offset);

            ulong* srcElem = (ulong*)src->Ptr; // 再取就是新地址，应该能拿到东西
            for (int i = 0; i < length; i++) {
                ulong writePos = srcElem[i];
                srcElem[i] = writePos + dryParam.baseAddr;
            }
        }

        public static void DryArray<T>(ref NativeSimpleArray<T> srcRef, DryParam dryParam) where T : unmanaged {
            var src = (NativeSimpleArray<T>*)UnsafeUtility.AddressOf(ref srcRef);
            int length = src->Length;
            if (length == 0) {
                return;
            }

            Debug.Assert(IsInRange((ulong)src->Ptr, dryParam.bufferStartAddr, dryParam.bufferEndAddr), "[Dry] ");
            // 自己内部指针偏移
            ulong addr = (ulong)src->Ptr - dryParam.baseAddr;
            src->UnsafeSetBufferPtr(addr);
        }

        public static void WetArray<T>(ref NativeSimpleArray<T> srcRef, DryParam dryParam) where T : unmanaged {
            var src = (NativeSimpleArray<T>*)UnsafeUtility.AddressOf(ref srcRef);
            int length = src->Length;
            if (length == 0) {
                return;
            }
            // 自己内部指针偏移
            ulong addr = (ulong)src->Ptr + dryParam.baseAddr;
            src->UnsafeSetBufferPtr(addr);
        }

        public static void SwitchDryNode(AGRuntimeType type, void* node, DryParam dryParam) {
            // node
            switch (type) {
                case AGRuntimeType.SlotNode: SlotNode.Dry(node, dryParam); break;

                case AGRuntimeType.ClipNode: ClipNode.Dry(node, dryParam); break;
                case AGRuntimeType.SequenceNode: SequenceNode.Dry(node, dryParam); break;
                case AGRuntimeType.BlendSpace2DNode: BlendSpace2DNode.Dry(node, dryParam); break;
                case AGRuntimeType.BlendSpace1DNode: BlendSpace1DNode.Dry(node, dryParam); break;

                case AGRuntimeType.SimpleBlendNode: SimpleBlendNode.Dry(node, dryParam); break;
                case AGRuntimeType.LayerBlendNode: LayerBlendNode.Dry(node, dryParam); break;
                case AGRuntimeType.SelectNode: SelectNode.Dry(node, dryParam); break;
                case AGRuntimeType.AxisBlend1DNode: AxisBlend1DNode.Dry(node, dryParam); break;
                case AGRuntimeType.RevolveNode: RevolveNode.Dry(node, dryParam); break;
                case AGRuntimeType.StateMachineNode: StateMachineNode.Dry(node, dryParam); break;
            }
            // perk：目前几种都没用需要操作的
        }

        public static void SwitchWetNode(AGRuntimeType type, void* node, DryParam dryParam) {
            switch (type) {
                case AGRuntimeType.SlotNode: SlotNode.Wet(node, dryParam); break;

                case AGRuntimeType.ClipNode: ClipNode.Wet(node, dryParam); break;
                case AGRuntimeType.SequenceNode: SequenceNode.Wet(node, dryParam); break;
                case AGRuntimeType.BlendSpace2DNode: BlendSpace2DNode.Wet(node, dryParam); break;
                case AGRuntimeType.BlendSpace1DNode: BlendSpace1DNode.Wet(node, dryParam); break;

                case AGRuntimeType.SimpleBlendNode: SimpleBlendNode.Wet(node, dryParam); break;
                case AGRuntimeType.LayerBlendNode: LayerBlendNode.Wet(node, dryParam); break;
                case AGRuntimeType.SelectNode: SelectNode.Wet(node, dryParam); break;
                case AGRuntimeType.AxisBlend1DNode: AxisBlend1DNode.Wet(node, dryParam); break;
                case AGRuntimeType.RevolveNode: RevolveNode.Wet(node, dryParam); break;
                case AGRuntimeType.StateMachineNode: StateMachineNode.Wet(node, dryParam); break;
            }
            // perk：目前几种都没用需要操作的
        }


        static bool IsInRange(ulong value, ulong left, ulong right) {
            return value >= left && value < right;
        }
    }

    #region Node

    public unsafe partial struct ClipNode {
        internal static void Dry(void* node, DryParam dryParam) { }
        internal static void Wet(void* node, DryParam dryParam) { }
    }

    public unsafe partial struct SimpleBlendNode {
        internal static void Dry(void* node, DryParam dryParam) { }
        internal static void Wet(void* node, DryParam dryParam) { }
    }

    public unsafe partial struct SlotNode {
        internal static void Dry(void* node, DryParam dryParam) {
            var self = (SlotNode*)node;
            DryUtility.DryArray(ref self->transitionEvaluator.weights, dryParam);
            DryUtility.DryArray(ref self->childrenGraphContext, dryParam);
        }
        internal static void Wet(void* node, DryParam dryParam) {
            var self = (SlotNode*)node;
            DryUtility.WetArray(ref self->transitionEvaluator.weights, dryParam);
            DryUtility.WetArray(ref self->childrenGraphContext, dryParam);
        }
    }

    public unsafe partial struct BlendSpace2DNode {
        internal static void Dry(void* node, DryParam dryParam) {
            var self = (BlendSpace2DNode*)node;
            DryUtility.DryArray(ref self->triangulationData.triangles, dryParam);
            DryUtility.DryArray(ref self->triangulationData.edges, dryParam);

            DryUtility.DryArray(ref self->weights, dryParam);
            DryUtility.DryArray(ref self->childPlayables, dryParam);
            DryUtility.DryArray(ref self->linkChildClips, dryParam);
        }
        internal static void Wet(void* node, DryParam dryParam) {
            var self = (BlendSpace2DNode*)node;
            DryUtility.WetArray(ref self->triangulationData.triangles, dryParam);
            DryUtility.WetArray(ref self->triangulationData.edges, dryParam);

            DryUtility.WetArray(ref self->weights, dryParam);
            DryUtility.WetArray(ref self->childPlayables, dryParam);
            DryUtility.WetArray(ref self->linkChildClips, dryParam);
        }
    }

    public unsafe partial struct BlendSpace1DNode {
        internal static void Dry(void* node, DryParam dryParam) {
            var self = (BlendSpace1DNode*)node;
            DryUtility.DryArray(ref self->weights, dryParam);
            DryUtility.DryArray(ref self->clipIds, dryParam);
            DryUtility.DryArray(ref self->clipsPos, dryParam);
            DryUtility.DryArray(ref self->childPlayables, dryParam);
            DryUtility.DryArray(ref self->linkChildClips, dryParam);
        }
        internal static void Wet(void* node, DryParam dryParam) {
            var self = (BlendSpace1DNode*)node;
            DryUtility.WetArray(ref self->weights, dryParam);
            DryUtility.WetArray(ref self->clipIds, dryParam);
            DryUtility.WetArray(ref self->clipsPos, dryParam);
            DryUtility.WetArray(ref self->childPlayables, dryParam);
            DryUtility.WetArray(ref self->linkChildClips, dryParam);
        }
    }

    public unsafe partial struct SequenceNode {
        internal static void Dry(void* node, DryParam dryParam) {
            var self = (SequenceNode*)node;
            DryUtility.DryArray(ref self->linkChildClips, dryParam);
            DryUtility.DryArray(ref self->clipSlices, dryParam);
            DryUtility.DryArray(ref self->weights, dryParam);
            DryUtility.DryArray(ref self->childPlayables, dryParam);
        }

        internal static void Wet(void* node, DryParam dryParam) {
            var self = (SequenceNode*)node;
            DryUtility.WetArray(ref self->linkChildClips, dryParam);
            DryUtility.WetArray(ref self->clipSlices, dryParam);
            DryUtility.WetArray(ref self->weights, dryParam);
            DryUtility.WetArray(ref self->childPlayables, dryParam);
        }
    }

    public unsafe partial struct SelectNode {
        internal static void Dry(void* node, DryParam dryParam) {
            var self = (SelectNode*)node;
            DryUtility.DryArray(ref self->transitionInfo.transitions, dryParam);
            DryUtility.DryArray(ref self->transitionEvaluator.weights, dryParam);
            DryUtility.DryArray(ref self->inputMapping.mapping, dryParam);
        }

        internal static void Wet(void* node, DryParam dryParam) {
            var self = (SelectNode*)node;
            DryUtility.WetArray(ref self->transitionInfo.transitions, dryParam);
            DryUtility.WetArray(ref self->transitionEvaluator.weights, dryParam);
            DryUtility.WetArray(ref self->inputMapping.mapping, dryParam);
        }
    }

    public unsafe partial struct StateMachineNode {
        internal static void Dry(void* node, DryParam dryParam) {
            var self = (StateMachineNode*)node;
            DryUtility.DryArray(ref self->transitionInfo.transitions, dryParam);
            DryUtility.DryTransitionConditionLinkArray(ref self->transitionInfo.conditions, dryParam);
            DryUtility.DryArray(ref self->transitionInfo.stateToFirstTransitionsIndex, dryParam);
            DryUtility.DryArray(ref self->transitionInfo.transitionsToFirstConditionIndex, dryParam);
            DryUtility.DryArray(ref self->transitionEvaluator.weights, dryParam);
        }

        internal static void Wet(void* node, DryParam dryParam) {
            var self = (StateMachineNode*)node;
            DryUtility.WetArray(ref self->transitionInfo.transitions, dryParam);
            DryUtility.WetTransitionConditionLinkArray(ref self->transitionInfo.conditions, dryParam);
            DryUtility.WetArray(ref self->transitionInfo.stateToFirstTransitionsIndex, dryParam);
            DryUtility.WetArray(ref self->transitionInfo.transitionsToFirstConditionIndex, dryParam);
            DryUtility.WetArray(ref self->transitionEvaluator.weights, dryParam);
        }
    }

    public unsafe partial struct RevolveNode {
        internal static void Dry(void* node, DryParam dryParam) {
            var self = (RevolveNode*)node;
            DryUtility.DryArray(ref self->transitionEvaluator.weights, dryParam);
        }

        internal static void Wet(void* node, DryParam dryParam) {
            var self = (RevolveNode*)node;
            DryUtility.WetArray(ref self->transitionEvaluator.weights, dryParam);
        }
    }

    public unsafe partial struct AxisBlend1DNode {
        internal static void Dry(void* node, DryParam dryParam) {
            var self = (AxisBlend1DNode*)node;
            DryUtility.DryArray(ref self->inputIds, dryParam);
            DryUtility.DryArray(ref self->portPos, dryParam);
        }

        internal static void Wet(void* node, DryParam dryParam) {
            var self = (AxisBlend1DNode*)node;
            DryUtility.WetArray(ref self->inputIds, dryParam);
            DryUtility.WetArray(ref self->portPos, dryParam);
        }
    }

    public unsafe partial struct LayerBlendNode {
        internal static void Dry(void* node, DryParam dryParam) {
            var self = (LayerBlendNode*)node;
            DryUtility.DryArray(ref self->linkWeights, dryParam);
            DryUtility.DryArray(ref self->layerTempData, dryParam);
        }

        internal static void Wet(void* node, DryParam dryParam) {
            var self = (LayerBlendNode*)node;
            DryUtility.WetArray(ref self->linkWeights, dryParam);
            DryUtility.WetArray(ref self->layerTempData, dryParam);
        }
    }

    #endregion

}
